Optimization
The Platypus library's NSGAII algorithm is used for constrained multi-objective optimization. The Genetic Algorithm is run for generations number of generations.
The three objectives are to maximize the Impression Rate, CTR, and Conversion Rate. The constraints are:
-
The sum of investment weights across all channels should not exceed 1
-
At least 10% of the total budget must be allocated to each channel.
-
Each Campaign Type is given some positive investment.
Since the Genetic Algorithm is randomized, it gives different results each time it is run. Therefore it is run iterations number of times and the solution with the highest product of the three quantities (latest moving averages of Impression Rate, CTR, and Conversion Rate) is taken as the final solution.
num_campaigns = df.shape[0]
impr, ctr, conv = list(np.array(df["Impression Rate moving avg"])), list(np.array(df["Click Through Rate moving avg"])), list(np.array(df["Conversion Rate moving avg"]))
class Marketing(Problem):
def __init__(self):
super().__init__(df.shape[0], 3, 1 + df.Platform.nunique())
self.types[:] = [Real(0.001, 1) for _ in range(num_campaigns)]
self.constraints[:] = ["<=1"] + [">=0.1" for i in range(df.Platform.nunique())]
def evaluate(self, solution):
campaigns = [df.Platform.value_counts()[channel] for channel in df.Platform.unique()]
total_impr = sum(solution.variables[i] * impr[i] for i in range(num_campaigns))
total_ctr = sum(solution.variables[i] * ctr[i] for i in range(num_campaigns))
total_conv = sum(solution.variables[i] * conv[i] for i in range(num_campaigns))
solution.constraints[:] = [sum(solution.variables)] + [sum(solution.variables[sum(campaigns[:i]):sum(campaigns[:i + 1])]) for i in range(df.Platform.nunique())]
solution.objectives[:] = [-total_impr, -total_ctr, -total_conv]
candidates = []
for i in range(iterations):
algorithm = NSGAII(Marketing())
algorithm.run(generations)
solution = nondominated(algorithm.result)
if solution:
solution = solution[0]
weights = solution.variables
IMPR = -1 * solution.objectives[0]
CTR = -1 * solution.objectives[1]
Conversion = -1 * solution.objectives[2]
candidates.append((weights, IMPR, CTR, Conversion, IMPR * CTR * Conversion))
solution = max(candidates, key=lambda x : x[-1])
weights, IMPR, CTR, Conversion = solution[0], solution[1], solution[2], solution[3]
Expected number of impressions, clicks and conversions are estimated:
final = pd.DataFrame({"Campaign type": df["Campaign type"].unique()[i], "Weight (%)": weights[i] * 100 / (sum(weights)), "Amount ($)": weights[i] * total_budget} for i in range(len(weights)))
final = pd.merge(df, final, on="Campaign type")
final["Expected Impressions"] = final["Amount ($)"] * final["Impression Rate moving avg"]
final["Expected Clicks"] = final["Expected Impressions"] * final["Click Through Rate moving avg"]
final["Expected Conversions"] = final["Expected Clicks"] * final["Conversion Rate moving avg"]
final = final[["Platform", "Campaign type", "Weight (%)", "Amount ($)", "Expected Impressions", "Expected Clicks", "Expected Conversions"]]
The total expected conversions is:
print(final["Expected Conversions"].sum())
# 13657.864853553792